﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krile.Data;

namespace Krile.Forms.MainForm.Controls.Surface
{
    public partial class Surface : UserControl
    {
        public Surface()
        {
            InitializeComponent();
            scroller.SmallChange = 10;
            compositor.OnVerticalPositionChanged += new Action(compositor_OnVerticalPositionChanged);
            compositor.OnStatusesChanged += new Action(compositor_OnStatusesChanged);
        }

        /// <summary>
        /// ステータス一覧
        /// </summary>
        internal XStatus[] Statuses
        {
            get { return compositor.Statuses; }
            set { compositor.Statuses = value; }
        }

        /// <summary>
        /// 選択されているステータス
        /// </summary>
        internal XStatus SelectedStatus
        {
            get { return compositor.SelectedStatus; }
        }

        internal long SelectedIndex
        {
            get { return compositor.SelectedIndex; }
            set { compositor.SelectedIndex = value; }
        }

        /// <summary>
        /// 選択を解除します。
        /// </summary>
        /// <param name="update">描画を更新するか</param>
        internal void Deselect(bool update)
        {
            compositor.Deselect(update);
        }

        //Applyクラッチ
        private static object aplClutchLocker = new object();
        private volatile bool applyClutch = false;

        //Scrollクラッチ
        private static object scrClutchLocker = new object();
        private volatile bool scrollClutch = false;

        /// <summary>
        /// Enumerate xstatus items in protected thread enumeration interruption
        /// </summary>
        /// <param name="callback">callback method</param>
        internal void DoEnumOperationWithProtectedThread(Action<XStatus> callback)
        {
            lock (compositor.statusesLocker)
            {
                foreach (var s in compositor.Statuses)
                {
                    callback(s);
                }
            }
        }

        void compositor_OnStatusesChanged()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(compositor_OnStatusesChanged));
                return;
            }
            SetScrollMax();
            SetScrollFromList();
        }

        void compositor_OnVerticalPositionChanged()
        {
            if (applyClutch) return;
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(compositor_OnVerticalPositionChanged));
                return;
            }

            if (scroller.Maximum != compositor.ListActualHeight)
                SetScrollMax();
            SetScrollFromList();
        }

        private void ApplyScroll()
        {
            lock (aplClutchLocker)
            {
                applyClutch = true;
                compositor.VerticalPosition = scroller.Value;
                applyClutch = false;
            }
        }

        private void SetScrollMax()
        {
            lock (scrClutchLocker)
            {
                scrollClutch = true;
                int maximum = compositor.ListActualHeight - compositor.Height;
                if (maximum < 0) maximum = 0;
                scroller.Maximum = maximum;
                if ((scroller.Enabled || scroller.Maximum == 0) && scroller.Value > maximum)
                {
                    scroller.Value = maximum;
                    ApplyScroll();
                }
                scrollClutch = false;
            }
        }

        private void SetScrollFromList()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(SetScrollFromList));
            }
            else
            {
                lock (aplClutchLocker)
                {
                    applyClutch = true;
                    if (scroller.Maximum < compositor.VerticalPosition)
                        compositor.VerticalPosition = scroller.Maximum;
                    scroller.Value = (int)compositor.VerticalPosition;
                    applyClutch = false;
                }
            }
        }

        internal int VerticalPosition
        {
            get { return scroller.Value; }
        }

        internal int VerticalPositionMax
        {
            get { return scroller.Maximum; }
        }

        internal void SetScrollTo(int value)
        {
            if (value > VerticalPositionMax)
                value = VerticalPositionMax;
            if (value < 0)
                value = 0;
            scroller.Value = value;
        }

        /// <summary>
        /// リストからスクロールバーの値を取って更新します。
        /// </summary>
        public void InitScrollbar()
        {
            compositor_OnVerticalPositionChanged();
        }

        internal int ScrollbarWidth
        {
            get { return scroller.Width; }
            set { scroller.Width = value; }
        }

        /// <summary>
        /// 表示画面を更新します。
        /// </summary>
        /// <param name="recomposite">リストの再構築をするか</param>
        public void SurfaceRedraw(bool recomposite)
        {
            compositor.Refresh(recomposite);
        }

        /// <summary>
        /// スクロール値
        /// </summary>
        internal int ScrollValue
        {
            get { return scroller.Value; }
            set
            {
                scroller.Value = value;
            }
        }

        private void scroller_Scroll(object sender, ScrollEventArgs e)
        {
            scroller_ValueChanged(sender, null);
        }

        private void scroller_ValueChanged(object sender, EventArgs e)
        {
            //check clutch
            bool sus = compositor.Refresher.IsSuspending;
            try
            {
                if (!sus)
                    compositor.Refresher.Suspend();
                if (scrollClutch) return;
                if (scroller.Maximum != compositor.ListActualHeight)
                    SetScrollMax();
                ApplyScroll();
            }
            finally
            {
                if (!sus)
                    compositor.Refresher.Restore(true);
            }
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            SetScrollMax();
            base.OnSizeChanged(e);
        }

        internal void EnsureVisible()
        {
            compositor.EnsureVisible();
        }

        internal bool SelectStatusByUID(ulong uid, bool p)
        {
            return compositor.SelectStatusByUID(uid, p);
        }

        internal void RefreshWeak()
        {
            compositor.Refresher.RefreshSurfaceDelay();
        }
    }
}
